package com.wangcanxuan.www.dao;

import com.wangcanxuan.www.annotation.orm.Column;
import com.wangcanxuan.www.annotation.orm.PrimaryKey;
import com.wangcanxuan.www.annotation.orm.Table;
import com.wangcanxuan.www.util.ConnectionPoolUtil;
import com.wangcanxuan.www.util.ReflectUtil;
import com.wangcanxuan.www.util.pool.PooledConnection;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.*;
import java.util.List;

/**
 * @author HHLJ
 * @date 2022/03/25
 */
public class SqlSessionImpl implements SqlSession {

    public static final ConnectionPoolUtil CONNECTION_POOL = ConnectionPoolUtil.getInstance();

    @Override
    public String getPrimaryKey(java.lang.Object obj) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        if (obj == null) {
            return null;
        }
        // 通过反射工具类获取该对象的所有属性
        List<Field> fields = ReflectUtil.getFields(obj);
        // 遍历fields，判断该属性是否含有PrimaryKey注解，返回该对象的主键的属性值
        for (Field field : fields) {
            if (field.isAnnotationPresent(PrimaryKey.class)) {
                String fieldName = field.getName();
                String getMethodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
                return String.valueOf(obj.getClass().getDeclaredMethod(getMethodName).invoke(obj));
            }
        }
        return null;
    }

    @Override
    public int insert(java.lang.Object obj) throws Exception {
        if (obj == null) {
            return 0;
        }
        // fieldNameToValue默认长度16
        Map<String, java.lang.Object> fieldNameToValue = new LinkedHashMap<>(16);
        // 进行字段的映射
        ReflectUtil.fieldMapper(obj, fieldNameToValue);
        // 拼接INSERT的sql语句
        StringBuilder front = new StringBuilder("INSERT INTO `" + obj.getClass().getAnnotation(Table.class).tableName() + "` (");
        StringBuilder tail = new StringBuilder("VALUES (");
        for (Map.Entry<String, java.lang.Object> field : fieldNameToValue.entrySet()) {
            front.append(field.getKey()).append(",");
            tail.append("?,");
        }
        front.setCharAt(front.length() - 1, ')');
        tail.setCharAt(tail.length() - 1, ')');
        String sql = front.append(tail).toString();
        // 进行更新操作
        return executeUpdate(sql, null, obj);
    }

    @Override
    public int delete(java.lang.Object obj) throws Exception {
        if (obj == null) {
            return 0;
        }
        // fieldNameToValue默认长度16
        Map<String, java.lang.Object> fieldNameToValue = new LinkedHashMap<>(16);
        // 进行字段的映射
        ReflectUtil.fieldMapper(obj, fieldNameToValue);
        // 拼接DELETE的sql语句
        StringBuilder sql = new StringBuilder("DELETE FROM `" + obj.getClass().getAnnotation(Table.class).tableName() + "` WHERE ");
        for (Map.Entry<String, java.lang.Object> field : fieldNameToValue.entrySet()) {
            sql.append(field.getKey()).append(" = ? AND ");
        }
        // 进行更新操作
        return executeUpdate(sql.substring(0, sql.length() - 4), null, obj);
    }

    @Override
    public int updateById(java.lang.Object obj) throws Exception {
        // 通过updateByMap返回更新结果
        return updateByMap(obj, null);
    }

    @Override
    public int updateByMap(java.lang.Object obj, Map<String, java.lang.Object> map) throws Exception {
        if (obj == null) {
            return 0;
        }
        // fieldNameToValue默认长度16
        Map<String, java.lang.Object> fieldNameToValue = new LinkedHashMap<>(16);
        // 进行字段的映射
        ReflectUtil.fieldMapper(obj, fieldNameToValue);
        // 拼接UPDATE的sql语句
        StringBuilder front = new StringBuilder("UPDATE `" + obj.getClass().getAnnotation(Table.class).tableName() + "` SET ");
        StringBuilder tail = new StringBuilder(" WHERE ");
        for (Map.Entry<String, java.lang.Object> field : fieldNameToValue.entrySet()) {
            front.append(field.getKey()).append(" = ?,");
        }
        front.setCharAt(front.length() - 1, ' ');
        // 判断map是否为空
        if (map == null) {
            tail.append(" id = '").append(getPrimaryKey(obj)).append("'");
            String sql = front.append(tail).toString();
            return executeUpdate(sql, null, obj);
        }
        for (Map.Entry<String, java.lang.Object> field : fieldNameToValue.entrySet()) {
            tail.append(field.getKey()).append(" = ").append(field.getValue()).append(" AND ");
        }
        String sql = front.append(tail).toString();
        // 进行更新操作
        return executeUpdate(sql.substring(0, sql.length() - 4), null, obj);
    }

    @Override
    public List<?> selectList(String sql, java.lang.Object[] params, java.lang.Object obj, Class<?> clazz) throws Exception {
        ResultSet resultSet = null;
        List<java.lang.Object> objectList = new LinkedList<>();
        try(PooledConnection pooledConnection = CONNECTION_POOL.getConnection();
            PreparedStatement preparedStatement = pooledConnection.connection.prepareStatement(sql)) {
            // 进行设置参数
            if (params != null && params.length > 0) {
                // 当注解中有手写的sql
                setParams(preparedStatement, params);
            } else {
                // 当注解中没有手写的sql
                setParams(preparedStatement, obj);
            }
            // 进行查询操作
            resultSet = preparedStatement.executeQuery();
            // 获取元数据
            ResultSetMetaData metaData = resultSet.getMetaData();
            // 获取查询到的字段的总列数
            int columnCount = metaData.getColumnCount();
            // 获取实体类的所有方法
            Method[] methods = clazz.getMethods();
            while (resultSet.next()) {
                java.lang.Object object = clazz.newInstance();
                for (int i = 1; i < columnCount + 1; i++) {
                    // 拼接setter，将下划线去掉
                    StringBuilder setMethodName = new StringBuilder("set");
                    String fieldName = metaData.getColumnName(i).replaceAll("[_]", "");
                    setMethodName.append(fieldName);
                    for (Method method : methods) {
                        // 无视大小写匹配方法
                        if (method.getName().equalsIgnoreCase(setMethodName.toString())) {
                            java.lang.Object value = resultSet.getObject(i);
                            method.invoke(object, value);
                        }
                    }
                }
                // 将查询到的对象添加到集合中
                objectList.add(object);
            }
            return objectList;
        } finally {
            if (resultSet != null) {
                resultSet.close();
            }
        }
    }

    @Override
    public java.lang.Object selectObject(String sql, java.lang.Object[] params, java.lang.Object obj, Class<?> clazz) throws Exception {
        // 通过selectList方法获取对象集合，使用三元运算符根据list.size()是否大于0来获得对象或者null
        List<?> list = selectList(sql, params, obj, clazz);
        return list.size() > 0 ? list.get(0) : null;
    }

    @Override
    public java.lang.Object selectField(String sql, java.lang.Object[] params, java.lang.Object obj) throws Exception {
        ResultSet resultSet = null;
        try(PooledConnection pooledConnection = CONNECTION_POOL.getConnection();
            PreparedStatement preparedStatement = pooledConnection.connection.prepareStatement(sql)
        ) {

            // 如果参数数组有参数，进行设置参数
            if (params != null && params.length > 0) {
                setParams(preparedStatement, params);
            }

            // 判断对象是否为空，设置参数
            if (obj == null) {
                setParams(preparedStatement, params);
            } else {
                setParams(preparedStatement, obj);
            }

            // 进行查询操作
            resultSet = preparedStatement.executeQuery();
            // 返回结果集第一个结果
            if (resultSet.next()) {
                return resultSet.getObject(1);
            }
        } finally {
            if (resultSet != null) {
                resultSet.close();
            }
        }
        return null;
    }

    @Override
    public String getSelectSql(java.lang.Object obj) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {
        // fieldNameToValue默认长度16
        Map<String, java.lang.Object> fieldNameToValue = new LinkedHashMap<>(16);
        // 进行字段映射
        ReflectUtil.fieldMapper(obj, fieldNameToValue);
        // 通过反射工具类获取该对象的所有属性
        List<Field> fields = ReflectUtil.getFields(obj);
        // 拼接SELECT的sql语句
        StringBuilder sql = new StringBuilder("SELECT ");
        for (Field field : fields) {
            sql.append(field.getAnnotation(Column.class).columnName()).append(",");
        }
        sql.setCharAt(sql.length() - 1, ' ');
        sql.append("FROM `").append(obj.getClass().getAnnotation(Table.class).tableName()).append("`");
        if (fieldNameToValue.size() > 0) {
            sql.append(" WHERE ");
            for (Map.Entry<String, java.lang.Object> field : fieldNameToValue.entrySet()) {
                sql.append(field.getKey()).append(" = ? AND ");
            }
            return sql.substring(0, sql.length() - 4);
        }
        return sql.toString();
    }

    @Override
    public void setParams(PreparedStatement preparedStatement, java.lang.Object[] params) throws SQLException {
        if (params == null) {
            return;
        }
        // 通过传入的params数组设置参数
        for (int i = 0; i < params.length; i++) {
            preparedStatement.setObject(i + 1, params[i]);
        }
    }

    @Override
    public void setParams(PreparedStatement preparedStatement, java.lang.Object obj) throws SQLException, InvocationTargetException, NoSuchMethodException, IllegalAccessException {
        // fieldNameToValue默认长度16
        Map<String, java.lang.Object> fieldNameMap = new LinkedHashMap<>(16);
        // 进行字段映射
        ReflectUtil.fieldMapper(obj, fieldNameMap);
        int count = 0;
        // 遍历fieldNameToValue来设置参数
        for (Map.Entry<String, java.lang.Object> field : fieldNameMap.entrySet()) {
            preparedStatement.setObject(++count, field.getValue());
        }
    }

    @Override
    public int executeUpdate(String sql, java.lang.Object[] params, java.lang.Object obj) throws Exception {
        int update;
        try(PooledConnection pooledConnection = CONNECTION_POOL.getConnection();
            PreparedStatement preparedStatement = pooledConnection.connection.prepareStatement(sql)
        ) {
            // 进行预编译
            // 判断对象是否为空
            if (obj != null) {
                setParams(preparedStatement, obj);
            }
            // 判断参数数组是否为空
            if (params != null) {
                setParams(preparedStatement, params);
            }
            update = preparedStatement.executeUpdate();
        }
        return update;
    }


}
